package znet

import (
	"fmt"
	"github.com/pkg/errors"
	"io"
	"my-zinx/utils"
	"my-zinx/ziface"
	"net"
	"sync"
)

type Connection struct {
	Server       ziface.IServer         // TcpServer
	Conn         *net.TCPConn           // TCP 套接字
	ConnID       uint64                 // 当前连接ID SessionID
	isClosed     bool                   // 连接状态：是否已关闭
	MsgHandler   ziface.IMsgHandle      // 多路由处理方法
	ExitBuffChan chan bool              // 退出管道
	msgChan      chan []byte            // 无缓冲 读写管道
	msgBuffChan  chan []byte            // 有缓冲 读写管道
	property     map[string]interface{} // 链接属性
	propertyLock sync.RWMutex           // 保护链接属性的锁
}

// NewConnection 创建一个新连接
func NewConnection(server ziface.IServer, conn *net.TCPConn, connID uint64,
	msgHandler ziface.IMsgHandle) ziface.IConnection {
	c := &Connection{
		Server:       server,
		Conn:         conn,
		ConnID:       connID,
		isClosed:     false,
		MsgHandler:   msgHandler,
		ExitBuffChan: make(chan bool, 1),
		msgChan:      make(chan []byte),
		msgBuffChan:  make(chan []byte, utils.Env.MaxMsgChanLen),
		property:     make(map[string]interface{}),
	}

	c.Server.GetConnMgr().Add(c)

	return c
}

func (c *Connection) Start() {
	// 起两个goroutine来处理连接后的读写数据
	go c.StartReader()
	go c.StartWriter()

	c.Server.CallOnConnStart(c)

	// 监听事件
	for {
		select {
		case <-c.ExitBuffChan:
			return
		}
	}
}

// StartReader 读协程
func (c *Connection) StartReader() {
	fmt.Println("[Reader Goroutine is running]")
	defer fmt.Printf("%s conn reader exit!\n", c.RemoteAddr().String())
	defer c.Stop()

	for {
		dp := NewDataPack()

		recvHead := make([]byte, dp.GetHeadLen())
		if _, err := io.ReadFull(c.Conn, recvHead); err != nil {
			fmt.Println("conn read head err: ", err)
			c.ExitBuffChan <- true
			return
		}

		recvMsg, err := dp.Unpack(recvHead)
		if err != nil {
			fmt.Println("msg unpack err: ", err)
			c.ExitBuffChan <- true
			return
		}

		recvData := make([]byte, recvMsg.GetDataLen())
		if recvMsg.GetDataLen() > 0 {
			if _, err := io.ReadFull(c.Conn, recvData); err != nil {
				fmt.Println("conn read data err: ", err)
				c.ExitBuffChan <- true
				return
			}
		}
		recvMsg.SetData(recvData)

		req := &Request{
			conn: c,
			msg:  recvMsg,
		}

		// V0.0.8 直接启动MsgHandler ==> 添加到任务队列
		if utils.Env.WorkerPoolSize > 0 {
			c.MsgHandler.SendMsgToTaskQueue(req)
		} else {
			go c.MsgHandler.DoMsgHandler(req)
		}
	}
}

// StartWriter 写协程
func (c *Connection) StartWriter() {
	fmt.Println("[Writer Goroutine is running]")
	defer fmt.Printf("%v conn writer exit!\n", c.RemoteAddr())

	for {
		select {
		case data := <-c.msgChan:
			// 写管道有数据
			if _, err := c.Conn.Write(data); err != nil {
				fmt.Println("send data err: ", err)
				return
			}
		case data, ok := <-c.msgBuffChan:
			if ok {
				if _, err := c.Conn.Write(data); err != nil {
					fmt.Println("send data err: ", err)
					return
				}
			} else {
				break
			}
		case <-c.ExitBuffChan:
			return
		}
	}
}

func (c *Connection) Stop() {
	if c.isClosed {
		return
	}

	c.isClosed = true

	// 如果用户注册了该连接的回调业务，在此调用执行
	c.Server.CallOnConnStop(c)

	_ = c.Conn.Close()

	// 通知管道
	c.ExitBuffChan <- true
	defer close(c.ExitBuffChan)
	defer close(c.msgBuffChan)

	// 从连接池中移除连接
	c.Server.GetConnMgr().Remove(c)
}

func (c *Connection) GetTCPConnection() *net.TCPConn {
	return c.Conn
}

func (c *Connection) GetConnID() uint64 {
	return c.ConnID
}

// RemoteAddr 远程客户端地址
func (c *Connection) RemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}

func (c *Connection) SendMsg(msgID uint64, data []byte) error {
	if c.isClosed {
		return errors.New("Conn is closed when send msg")
	}

	dp := NewDataPack()
	buf, err := dp.Pack(NewMessage(msgID, data))
	if err != nil {
		fmt.Println("pack err: ", err)
		return errors.Wrap(err, "Pack error")
	}

	//if _, err := c.Conn.Write(buf); err != nil {
	//	fmt.Println("write err: ", err)
	//	c.ExitBuffChan <- true
	//	return err
	//}

	// V0.0.7: 写数据给客户端 ==> 写数据到读写管道
	c.msgChan <- buf

	return nil
}

func (c *Connection) SendBuffMsg(msgID uint64, data []byte) error {
	if c.isClosed {
		return errors.New("Conn is closed when send buff msg")
	}

	dp := NewDataPack()
	buf, err := dp.Pack(NewMessage(msgID, data))
	if err != nil {
		fmt.Println("pack err: ", err)
		return errors.Wrap(err, "Pack error")
	}

	c.msgBuffChan <- buf
	return nil
}

func (c *Connection) SetProperty(key string, value interface{}) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()

	c.property[key] = value
}

func (c *Connection) GetProperty(key string) (interface{}, error) {
	c.propertyLock.RLock()
	defer c.propertyLock.RUnlock()

	value, ok := c.property[key]
	if !ok {
		return nil, errors.New("no property found")
	}
	return value, nil
}

func (c *Connection) RemoveProperty(key string) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()

	delete(c.property, key)
}
